//===============================================================================
// Microsoft patterns & practices
// Mobile Client Software Factory - July 2006
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;

namespace Microsoft.Practices.Mobile.Configuration.Tests
{
	[TestClass]
	public class ConfigurationElementFixture
	{
		private TSection GetSection<TSection>(string xml, string sectionName)
			where TSection : ConfigurationSection
		{
			string sectionXml = String.Format(xml, typeof(TSection).AssemblyQualifiedName);
			StringReader reader = new StringReader(sectionXml);

			Configuration configuration = new Configuration(XmlReader.Create(reader));

			ConfigurationManager.Configuration = configuration;

			return (TSection)ConfigurationManager.GetSection(sectionName);
		}

		[TestMethod]
		public void ConfigurationElementCanHandleUnrecognizedAttributes()
        {
			MockSection section = GetSection<MockSection>(@"
					<configuration>
						<configSections>
							<section name=""MyCustomSection"" type=""{0}"" />
						</configSections>
						<MyCustomSection>
							<providers>
								<add name=""Foo"" bar=""baz"" />
							</providers>
						</MyCustomSection>
					</configuration>", "MyCustomSection");

			Assert.AreEqual(1, section.Providers.Count);
			Assert.AreEqual("baz", section.Providers["Foo"].Attributes["bar"]);

		}

        [TestMethod]
        public void CanSerializeBoleanValues()
        {
            MockBoleanSection section = GetSection<MockBoleanSection>(@"
					<configuration>
						<configSections>
							<section name=""boolSection"" type=""{0}"" />
						</configSections>
						<boolSection bool1=""true"" bool2=""false"" bool3=""1"" bool4=""0"" />
					</configuration>", "boolSection");

            Assert.IsNotNull(section);
            Assert.IsTrue(section.Boolean1);
            Assert.IsFalse(section.Boolean2);
            Assert.IsTrue(section.Boolean3);
            Assert.IsFalse(section.Boolean4);
        }

        [TestMethod]
        public void ThrowsPropperExceptionIfFormatExceptionWhenParsingConfiguration()
        {
            ConfigurationManager.ClearCache();
            ConfigurationManager.ConfigFileName = "App.config";

            try
            {
                MockIntSection section = GetSection<MockIntSection>(@"
					<configuration>
						<configSections>
							<section name=""intSection"" type=""{0}"" />
						</configSections>
						<intSection int=""InvalidInt"" />
					</configuration>", "intSection");

                Assert.IsNotNull(section);
            }
            catch (ConfigurationErrorsException e)
            {
                Assert.IsTrue(e.Message.Contains("Int"), "Property name should be included in the error message");
                Assert.IsTrue(e.Message.Contains("InvalidInt"), "Value being parsed should be included in the error message");
                Assert.IsTrue(e.Message.Contains("Int32"), "Target type should be included in the error message");
                Assert.IsTrue(e.Message.Contains("FormatException"), "Orginal exception type should be included in the error message");

                return;
            }

            Assert.Fail("Did not throw ConfigurationErrorsException as expected");
        }

        [TestMethod]
        public void ThrowsPropperExceptionIfOverflowExceptionWhenParsingConfiguration()
        {
            try
            {
                MockIntSection section = GetSection<MockIntSection>(@"
					<configuration>
						<configSections>
							<section name=""intSection"" type=""{0}"" />
						</configSections>
						<intSection int=""999999999999999999999999999999999999"" />
					</configuration>", "intSection");

                Assert.IsNotNull(section);
            }
            catch (ConfigurationErrorsException e)
            {
                Assert.IsTrue(e.Message.Contains("Int"), "Property name should be included in the error message");
                Assert.IsTrue(e.Message.Contains("999999999999999999999999999999999999"), "Value being parsed should be included in the error message");
                Assert.IsTrue(e.Message.Contains("Int32"), "Target type should be included in the error message");
                Assert.IsTrue(e.Message.Contains("OverflowException"), "Orginal exception type should be included in the error message");

                return;
            }

            Assert.Fail("Did not throw ConfigurationErrorsException as expected");
        }

		class MockSection : ConfigurationSection
		{
			[ConfigurationProperty("providers")]
			public MockCollection Providers
			{
				get { return (MockCollection)this["providers"]; }
				set { this["providers"] = value; }
			}
		}

		class MockCollection : ConfigurationElementCollection
		{
			protected override object GetElementKey(ConfigurationElement element)
			{
				return ((MockElement)element).Name;
			}

			protected override ConfigurationElement CreateNewElement()
			{
				return new MockElement();
			}

            /// <summary>
            ///	Gets an element from the collection with the given key.
            /// </summary>
            public new MockElement this[string name]
            {
                get { return (MockElement)BaseGet(name); }
            }
		}

		class MockElement : ConfigurationElement
		{
			public Dictionary<string, string> Attributes = new Dictionary<string, string>();

			protected override bool OnDeserializeUnrecognizedAttribute(string name, string value)
			{
				Attributes.Add(name, value);
				return true;
			}

			[ConfigurationProperty("name")]
			public string Name
			{
				get { return (string)this["name"]; }
				set { this["name"] = value; }
			}
		}

        class MockBoleanSection : ConfigurationSection
        {
            [ConfigurationProperty("bool1")]
            public bool Boolean1
            {
                get { return (bool)this["bool1"]; }
            }

            [ConfigurationProperty("bool2")]
            public bool Boolean2
            {
                get { return (bool)this["bool2"]; }
            }

            [ConfigurationProperty("bool3")]
            public bool Boolean3
            {
                get { return (bool)this["bool3"]; }
            }

            [ConfigurationProperty("bool4")]
            public bool Boolean4
            {
                get { return (bool)this["bool4"]; }
            }
        }

        class MockIntSection : ConfigurationSection
        {
            [ConfigurationProperty("int")]
            public int Int
            {
                get { return (int)this["int"]; }
            }
        }
	}
}
